#pragma once


#include "qtypes.h"
#include "qdebug.h"



namespace micro
{
namespace utils
{



class QStringBuffer
{
private:
    // Data.
    LONG	m_nRefCount;
    int		m_nLength;
    int		m_nMax;
    
    QStringBuffer () {};
public:
    // Reference counting.
    inline LONG AddRef()
    {
        ::InterlockedIncrement( &m_nRefCount );
        return m_nRefCount;
    }
    inline LONG Release()
    {
        ::InterlockedDecrement( &m_nRefCount );
        return m_nRefCount;
    }
    inline LONG GetRefs() const
    {
        return m_nRefCount;
    }
    inline bool IsShared() const
    {
        return ( bool )( m_nRefCount > 1 );
    }
    
    
    // Properties.
    inline int GetLength() const
    {
        return m_nLength;
    }
    inline TCHAR* GetString() const
    {
        return ( TCHAR* )( this + 1 );
    }
    inline int GetMax() const
    {
        return m_nMax;
    }
    
    
    // Set string length. Values must lie between
    // 0 and the current length or buffer maximum.
    inline void SetLength( int nNewLen, bool OnMax = FALSE )
    {
        // Assert when the new length is larger than the old length
        // or when the length is larger than the maximum.
#ifdef _DEBUG
        if ( OnMax )
            ASSERT( nNewLen >= 0 && nNewLen <  m_nMax  );
        else
            ASSERT( nNewLen >= 0 && nNewLen <= m_nLength  );
            
#endif
        m_nLength = nNewLen;
        GetString()[ nNewLen ] = _T( '\0' );
    }
    
    // Allocate a new string buffer.
    static QStringBuffer* AllocBuffer( int nChars )
    {
    
        // This should avoid heap-fragmentation a bit.
        int	nMax = 0;
        
        if		( nChars < 16  )
            nMax = 16;
        else
            if ( nChars < 32  )
                nMax = 32;
            else
                if ( nChars < 64  )
                    nMax = 64;
                else
                    if ( nChars < 128 )
                        nMax = 128;
                    else
                        if ( nChars < 256 )
                            nMax = 256;
                        else
                            if ( nChars < 320 )
                                nMax = 320;
                            else
                                if ( nChars < 384 )
                                    nMax = 384;
                                else
                                    if ( nChars < 448 )
                                        nMax = 448;
                                    else
                                        if ( nChars < 512 )
                                            nMax = 512;
                                        else
                                            nMax = ( ( nChars + 7 ) & -8 ) + 8 ;
                                            
        // Allocate the memory required to hold the object and
        // the string including a termination 0-character.
        UINT sz = sizeof( QStringBuffer ) + ( ( 1 + nMax )  * sizeof( TCHAR ) );
        QStringBuffer* pStr = ( QStringBuffer* ) new BYTE[ sz ];
        memset( pStr, 0, sz );
        
        // Setup the object.
        pStr->m_nRefCount = 1;
        pStr->m_nLength	  = nChars;
        pStr->m_nMax	  = nMax;
        
        
        // Return a pointer to it.
        return pStr;
    }
    
    // Free a string buffer.
    static void FreeBuffer( QStringBuffer* pStr )
    {
        // Free the occupied memory.
        ASSERT( pStr );
        delete [] ( BYTE* )pStr;
    }
    
    
};





class QString
{
    // Data.
    TCHAR*		m_pszString;
    
public:
    // Construction.
    QString();
    QString( const QString& csSource );
    QString( TCHAR ch, int nRepeat = 1 );
    
    QString(  LONG_PTR csSource );
    
    
    QString( LPCTSTR pszSource, int nChars = -1 );
    QString( LPCSTR pszSource, int nChars = -1 );
    //char * GetMBS(){ return nullptr; }
    
    // Destruction.
    virtual ~QString();
    
    // Implementation.
    void Empty();
    void SetStringLength( int nLength );
    bool IsEqual( const QString& csSource ) const;
    bool IsEqual( LPCTSTR pszSource ) const;
    LPTSTR GetBuffer( int nChars );
    void AllocateBuffer( int nChars, bool bCopy = FALSE );
    TCHAR GetAt( int nIndex ) const;
    TCHAR& GetAt( int nIndex );
    void SetAt( int nIndex, TCHAR ch );
    int Compare( LPCTSTR pszString ) const;
    int CompareNoCase( LPCTSTR pszString ) const;
    


    int NatCompare( LPCTSTR pszString ) const;
    int NatCompareNoCase( LPCTSTR pszString ) const;
    
    static int Natcmp ( LPCTSTR  a, LPCTSTR b );
    static int Naticmp( LPCTSTR  a, LPCTSTR b );
    
    
    int Collate( LPCTSTR pszString ) const;
    int CollateNoCase( LPCTSTR pszString ) const;
    QString Left( int nCount ) const;
    QString Mid( int nIndex, int nCount = -1 ) const;
    QString Right( int nCount ) const;
    
    void UpperCase();
    void LowerCase();
    void TitleCase();
    void SwapCase();
    
    int Replace( TCHAR chOld, TCHAR chNew );
    int Remove( TCHAR ch );
    int Insert( int nIndex, TCHAR ch );
    int Insert( int nIndex, LPCTSTR pszString );
    int Insert( int nIndex, QString& csString );
    int Delete( int nIndex, int nCount = 1 );
    int Replace( const QString&  oldString, const QString& newString );
    

	int Find( TCHAR ch, int nIndex = 0 ) const;
    int Find( LPCTSTR pszString, int nIndex = 0 ) const;
	int FindNoCase( LPCTSTR pszString, int nIndex = 0 ) const;
    int ReverseFind( TCHAR ch ) const;
    int FindOneOf( LPCTSTR pszCharSet, int nIndex = 0 ) const;
    int ReverseFindOneOf( LPCTSTR pszCharSet  ) const;

    
    int GetDelimitedPart( TCHAR ch, int nIndex, QString& str ) const;
    inline int GetLength() const
    {
        return m_pszString ? GetBuf()->GetLength() : 0;
    }
    inline int GetByteSize() const
    {
        return GetLength() * sizeof( TCHAR );
    }
    inline bool IsEmpty() const
    {
        return ( bool )( ( m_pszString == nullptr ) || ( GetBuf()->GetLength() == 0 ) );
    }
    
    
    QString& Trim();
    
    void TrimLeft( TCHAR chTarget );
    void TrimRight( TCHAR chTarget );
    
    
    bool IsSeparator( TCHAR c );
    void RemoveSeparatorsRight();
    void RemoveSeparatorsLeft();
    
    
    
    
    
    //tools
    void AddBackslash();
    void RemoveBackslash();
    
    void FormatMessage( DWORD dwErrorID = 0 );
    void Format( LPCTSTR pszFormatString, ... );
    
    
    bool LoadString( UINT uID );




public:
    
    // Conversion operator overloads.
    inline operator LPCTSTR() const
    {
        return m_pszString ? m_pszString : _T( "" );
    }
    inline operator LPTSTR()
    {
        return m_pszString ? m_pszString : _T( "" );
    }
    //inline operator BSTR()				{ return m_pszString ? m_pszString : _T( "" ); }
    
    
    // Index operator overload.
    inline TCHAR operator[]( int nIndex ) const
    {
        return GetAt( nIndex );
    }
    inline TCHAR& operator[]( int nIndex )
    {
        return GetAt( nIndex );
    }
    
    // Assignement operator overloads.
    const QString& operator=( const QString& csSource );
    const QString& operator=( LPCSTR pszSource );
    const QString& operator=( LPCTSTR pszSource );
    
    const QString& operator=( TCHAR ch );
    
    
    
    // Addition operator overloads.
    const QString& operator+=( const QString& csSource );
    const QString& operator+=( LPCTSTR csSource );
    const QString& operator+=( LPCSTR  pszSource );
    
    const QString& operator+=( TCHAR ch );
    
    
    // Comparison operator overloads.
    bool operator==( const QString& s  ) const
    {
        return IsEqual( s );
    }
    bool operator==( LPCTSTR pszSource ) const
    {
        return IsEqual( pszSource );
    }
    bool operator!=( const QString& s  ) const
    {
        return !IsEqual( s );
    }
    bool operator!=( LPCTSTR pszSource ) const
    {
        return !IsEqual( pszSource );
    }
    
    
    bool operator>=( const QString& csSource ) const;
    bool operator>=( LPCTSTR pszSource ) const;
    bool operator>( const QString& csSource ) const;
    bool operator>( LPCTSTR pszSource ) const;
    bool operator<( const QString& csSource ) const;
    bool operator<( LPCTSTR pszSource ) const;
    bool operator<=( const QString& csSource ) const;
    bool operator<=( LPCTSTR pszSource ) const;
    

	QString digestToHex( const unsigned char * bytes, int n );

private:
    // Helpers.
    void CopyBeforeWrite();
    void Release();
    
    
    //void DoConversion();
    inline const QStringBuffer* GetBuf() const
    {
        return ( ( QStringBuffer* )m_pszString ) - 1;
    }
    inline QStringBuffer* GetBuf()
    {
        return ( ( QStringBuffer* )m_pszString ) - 1;
    }
    
	//static
private:
	static int compare_right( LPCTSTR  a, LPCTSTR  b );
	static int compare_left( LPCTSTR a, LPCTSTR  b );
	static int strnatcmp0( LPCTSTR  a, LPCTSTR  b, bool icase = false );
};




// Addition operators.
inline QString operator+( const QString& str1, const QString& str2 )
{
    QString s( str1 );
    s += str2;
    return s;
}
inline QString operator+( const QString& str1,   TCHAR ch )
{
    QString s( str1 );
    s += ch;
    return s;
}
inline QString operator+( const QString str1, LPCTSTR str2 )
{
    QString s( str1 );
    s += str2;
    return s;
}
inline QString operator+( LPCTSTR str1, const QString str2 )
{
    QString s( str1 );
    s += str2;
    return s;
}



//Ascii string
class QCharString
{
    char* m_str;
public:
    QCharString()
    {
        m_str = nullptr;
    }
    QCharString( const QString& str )
    {
        m_str = nullptr;
        
        if ( str.IsEmpty() )
            return;
            
        int len = str.GetLength() ;
        // Allocate a buffer to hold the conversion.
        m_str = new char[ 2 * len ];
        
        size_t sz = 0;
        // Do the actual conversion.
        wcstombs_s( &sz, m_str, 2 * len,  str, len );
        
    }
    ~QCharString()
    {
        if ( m_str )
            delete [] m_str;
    }
    
    inline operator LPSTR()
    {
        return m_str ? m_str : ( "" );
    }
    inline operator LPCSTR()
    {
        return m_str ? m_str : ( "" );
    }
    
};

}

}//micro